package com.dao.hibernate;

import com.bean.Entity;
import com.dao.Crud;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: Stanislav
 * Date: 6/19/12
 * Time: 12:49 PM
 * To change this template use File | Settings | File Templates.
 */
public abstract class BaseDaoImpl<T extends Entity> implements Crud<T> {
    private static SessionFactory sessionFactory = null;
    private static int bulkSize = 100;
    private Session session;

    public BaseDaoImpl(){
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (HibernateException e) {
            e.printStackTrace();
        }
    }

    protected Session getSession() {
        Session session = null;
        try {
            session = sessionFactory.openSession();
        } catch (HibernateException e) {
            e.printStackTrace();
        }
        return session;
    }

    @Override
    public int insert(final T object) {
        return execute(new UnitOfWork<Integer>() {
            @Override
            public Integer doWork() {
                session.save(object);
                return object.getId();
            }
        });
    }

    @Override
    public List<Integer> insert(final List<T> objects) {
        return execute(new UnitOfWork<List<Integer>>() {
            @Override
            public List<Integer> doWork() {
                int i = 0;
                List<Integer> result = new ArrayList<Integer>();
                for (T obj : objects) {
                    session.save(obj);
                    i++;
                    if (i % bulkSize == 0) {
                        session.flush();
                        session.clear();
                    }
                    result.add(obj.getId());
                }

                return result;
            }
        });
    }

    @Override
    public int delete(final T object) {
        return execute(new UnitOfWork<Integer>() {
            @Override
            public Integer doWork() {
                session.delete(object);
                return 1;
            }
        });
    }

    @Override
    public int delete(final List<T> objects) {
        return execute(new UnitOfWork<Integer>() {
            @Override
            public Integer doWork() {
                int i = 0;
                for (T obj : objects) {
                    session.delete(obj);
                    i++;
                    if (i % bulkSize == 0) {
                        session.flush(); //persist
                        session.clear(); //and clean memory
                    }
                }
                return objects.size();
            }
        });
    }

    @Override
    public boolean deleteAll() {
        return execute(new UnitOfWork<Boolean>() {
            @Override
            public Boolean doWork() {
                session.createQuery("delete from " + getAssociatedClass().getName()).executeUpdate();
                return true;
            }
        });
    }

    @Override
    public boolean update(final T object) {
        return execute(new UnitOfWork<Boolean>() {
            @Override
            public Boolean doWork() {
                session.update(object);
                return true;
            }
        });
    }

    @Override
    public T find(final Integer id) {
        return execute(new UnitOfWork<T>() {
            @Override
            public T doWork() {
                return (T)session.load(getAssociatedClass(), id);
            }
        });
    }

    protected abstract Class getAssociatedClass();

    @Override
    public List<T> selectAll() {
        return execute(new UnitOfWork<List<T>>() {
            @Override
            public List<T> doWork() {
                return session.createQuery("from " + getAssociatedClass().getName()).list();
            }
        });
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
    }
    
    protected <T> T execute(UnitOfWork<T> aUnitOfWork){
        T result = null;
        session = this.getSession();
        Transaction transaction = session.beginTransaction();
        try {
            result = aUnitOfWork.doWork();
            transaction.commit();
        } catch (HibernateException e) {
            e.printStackTrace();
            transaction.rollback();
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (HibernateException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    protected interface UnitOfWork<T> {
        T doWork();
    }
}
